﻿namespace Nomo.UnityCoreModule
{
    /// <summary>
    /// 音乐管理器。
    /// </summary>
    public sealed class MusicManager : SingletonBehaviour<MusicManager>
    {
        private enum MusicState : byte
        {
            None    = 0,
            Playing = 1,
            Paused  = 2
        }

        private UnityEngine.AudioSource _audioSource;
        private MusicState              _state;
        private string                  _currentKey;

        /// <summary>
        /// 在开始播放时要执行的方法。
        /// </summary>
        public event System.Action<string> Played;

        /// <summary>
        /// 在暂停时要执行的方法。
        /// </summary>
        public event System.Action<string> Paused;

        /// <summary>
        /// 在取消暂停时要执行的方法。
        /// </summary>
        public event System.Action<string> UnPaused;

        /// <summary>
        /// 在停止播放时要执行的方法。
        /// </summary>
        public event System.Action<string> Stopped;

        /// <summary>
        /// 正在播放吗？
        /// </summary>
        public bool IsPlaying => _state == MusicState.Playing;

        private UnityEngine.AudioSource AudioSource
        {
            get
            {
                if (_audioSource == default)
                {
                    _audioSource = gameObject.AddComponent<UnityEngine.AudioSource>();
                }

                return _audioSource;
            }
        }

        /// <summary>
        /// 是否静音？
        /// </summary>
        public bool Mute { get => AudioSource.mute; set => AudioSource.mute = value; }

        /// <summary>
        /// 音量。
        /// </summary>
        public float Volume { get => AudioSource.volume; set => AudioSource.volume = value; }

        /// <summary>
        /// 是否循环播放？
        /// </summary>
        public bool Loop { get => AudioSource.loop; set => AudioSource.loop = value; }

        /// <summary>
        /// 播放最近的音乐。
        /// </summary>
        public void Play()
        {
            Play(_currentKey, true);
        }

        /// <summary>
        /// 播放指定的音乐。
        /// </summary>
        /// <param name="key">音乐名。</param>
        /// <param name="constraint">如果指定的音乐正在播放，要让它从头开始播放吗？</param>
        public void Play(string key, bool constraint = false)
        {
            if (key == null)
            {
                return;
            }

            switch (_state)
            {
                case MusicState.Playing when !constraint && key == _currentKey:
                    break;
                case MusicState.Paused when !constraint && key == _currentKey:
                    AudioSource.UnPause();
                    _state = MusicState.Playing;
                    UnPaused?.Invoke(_currentKey);
                    return;
                case MusicState.Playing:
                case MusicState.Paused:
                    Stop();
                    goto case MusicState.None;
                case MusicState.None:
                    var audioClip = AudioProvider.Instance.GetAudioClip(key);
                    if (audioClip == default)
                    {
                        return;
                    }
                    AudioSource.clip = audioClip;
                    AudioSource.Play();
                    _currentKey = key;
                    _state      = MusicState.Playing;
                    Played?.Invoke(_currentKey);
                    break;
                default:
                    throw new System.ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// 暂停。
        /// </summary>
        public void Pause()
        {
            switch (_state)
            {
                case MusicState.None:
                case MusicState.Paused:
                    return;
                case MusicState.Playing:
                    AudioSource.Pause();
                    _state = MusicState.Paused;
                    Paused?.Invoke(_currentKey);
                    break;
                default:
                    throw new System.ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// 取消暂停。
        /// </summary>
        public void UnPause()
        {
            switch (_state)
            {
                case MusicState.None:
                case MusicState.Playing:
                    return;
                case MusicState.Paused:
                    AudioSource.UnPause();
                    _state = MusicState.Playing;
                    UnPaused?.Invoke(_currentKey);
                    break;
                default:
                    throw new System.ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// 停止播放。
        /// </summary>
        public void Stop()
        {
            switch (_state)
            {
                case MusicState.None:
                    return;
                case MusicState.Playing:
                case MusicState.Paused:
                    AudioSource.Stop();
                    _state = MusicState.None;
                    Stopped?.Invoke(_currentKey);
                    break;
                default:
                    throw new System.ArgumentOutOfRangeException();
            }
        }

        private void Update()
        {
            if (_state != MusicState.Playing || AudioSource.isPlaying)
            {
                return;
            }

            _state = MusicState.None;
            Stopped?.Invoke(_currentKey);
        }
    }
}
